/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    // 哈希表存储字符最后出现的索引
    const charMap = new Map();
    let maxLen = 0;  // 最长无重复子串长度
    let left = 0;    // 滑动窗口左指针

    // 右指针遍历字符串
    for (let right = 0; right < s.length; right++) {
        const currentChar = s[right];
        
        // 若当前字符已存在于哈希表中，且索引 >= left（在当前窗口内）
        if (charMap.has(currentChar) && charMap.get(currentChar) >= left) {
            // 移动左指针到重复字符的下一位（保证窗口内无重复）
            left = charMap.get(currentChar) + 1;
        }
        
        // 更新当前字符的最新索引
        charMap.set(currentChar, right);
        
        // 计算当前窗口长度（right - left + 1），更新最大长度
        maxLen = Math.max(maxLen, right - left + 1);
    }
    
    return maxLen;
};